using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.IO;
using System.Xml;
using System.Diagnostics;

namespace Woz
{
    /// <summary>
    /// a screen thumbnail basically contains a thumbnail of the screen
    /// </summary>
    public class ScreenThumbnail : UserControl
    {
        /// <summary>
        /// static constructor to initialize all of the static members
        /// </summary>
        static ScreenThumbnail()
        {
            sPadding = new Padding(3);

            sImageRectangle.Size = Woz.StaticVariables.Instance.ThumbnailImageSize;
            sImageRectangle.Location = new Point(sPadding.Left, sPadding.Right);

            sLabelRectangle.Size = new Size(sImageRectangle.Width, 16);
            sLabelRectangle.Location = new Point(sPadding.Left, sImageRectangle.Top + sImageRectangle.Height + sPadding.Top + 1);

            // also initialize the textbox
            sRenamingTextbox = new TextBox();
            sRenamingTextbox.TextAlign = HorizontalAlignment.Center;
            sRenamingTextbox.Size = new Size(sImageRectangle.Width, 17);
            sRenamingTextbox.Leave += cRenamingTextboxLostFocus;
            sRenamingTextbox.KeyDown += cRenamingTextboxKeyDown;

            sSize = new Size(sImageRectangle.Width + sPadding.Horizontal, sLabelRectangle.Top + sLabelRectangle.Height + sPadding.Bottom);

            // create the background image for selected thumbnails
            Woz.ScreenThumbnail.sSelectedImage = new Bitmap(sSize.Width, sSize.Height);
            using (Graphics graphics = Graphics.FromImage(sSelectedImage))
            {
                graphics.Clear(Color.Orange);
                graphics.FillRectangle(new SolidBrush(Color.FromArgb(255, 212, 139)), 1, 1, sSize.Width - 2, sSize.Height - 2);
            }

            // create the background image for the mouse overed thumbnails
            Woz.ScreenThumbnail.sMouseOveredImage = new Bitmap(sSize.Width, sSize.Height);
            using (Graphics graphics = Graphics.FromImage(sMouseOveredImage))
            {
                graphics.Clear(Color.FromArgb(49, 106, 197));
                graphics.FillRectangle(new SolidBrush(Color.FromArgb(193, 210, 238)), 1, 1, sSize.Width - 2, sSize.Height - 2);
            }

            sStringFormat = new StringFormat();
            sStringFormat.Alignment = StringAlignment.Center;
        }

        private static void cRenamingTextboxKeyDown(object pSender, KeyEventArgs pEventArgs)
        {
            if (pEventArgs.KeyCode == Keys.Escape)
            {
                DetachTextbox(true);
            }
            else if (pEventArgs.KeyCode == Keys.Enter)
            {
                DetachTextbox(false);
            }
        }

        private static void cRenamingTextboxLostFocus(object pSender, EventArgs pEventArgs)
        {
            // detach the textbox
            DetachTextbox(false);
        }

        /// <summary>
        /// represents a screen thumbnail
        /// </summary>
        /// <param name="pScreen">the screen whose thumbnail we need</param>
        internal ScreenThumbnail(Woz.Screen pScreen)
        {
            this.Margin = new Padding(0);

            // set the screen
            this.mScreen = pScreen;

            // set the size on ourselves
            this.Size = sSize;

            this.MouseMove += new MouseEventHandler(this.cMouseMoved);
            this.MouseEnter += new EventHandler(this.cMouseEntered);
            this.MouseLeave += new EventHandler(this.cMouseLeave);
            this.Click += new EventHandler(this.cClicked);
            this.DoubleClick += this.cDoubleClicked;

            // initialize highlight on mouseover
            this.mHighlightOnMouseOver = true;
        }

        /// <summary>
        /// gets or sets whether the screen thumbnail is selected
        /// </summary>
        public bool Selected
        {
            get
            {
                return this.mSelected;
            }

            set
            {
                // eliminate redundant selecting
                if (this.mSelected == value)
                {
                    return;
                }

                // this is a new state so assign it to our member
                this.mSelected = value;

                // check if we are now selected
                if (this.mSelected)
                {
                    // update our background image
                    this.BackgroundImage = sSelectedImage;
                }

                // or check if we want to unselect
                else
                {
                    // check if were highlighted or mouse overed
                    if ((this.mMouseOvered || this.mHighlighted) && this.mHighlightOnMouseOver)
                    {
                        this.BackgroundImage = sMouseOveredImage;
                    }

                    // or just set the background image to nothing
                    else
                    {
                        this.BackgroundImage = null;
                    }
                }
            }
        }

        /// <summary>
        /// gets or sets whether the screen thumbnail is highlighted
        /// </summary>
        public bool Highlighted
        {
            get
            {
                return this.mHighlighted;
            }

            set
            {
                // eliminate redundant selecting
                if(this.mHighlighted == value)
                {
                    return;
                }

                // this is a new state so assign it to our member
                this.mHighlighted = value;

                // check if we are now highlighted
                if (this.mHighlighted)
                {
                    // make sure we arent selected
                    if(!this.mSelected)
                    {
                        this.BackgroundImage = sMouseOveredImage;
                    }
                }

                // or check if we are now unhighlighted
                else
                {
                    // make sure were not selected or mouse overed
                    if(!this.mSelected && !this.mMouseOvered)
                    {
                        this.BackgroundImage = null;
                    }
                }
            }
        }

        private void cMouseMoved(object pSender, MouseEventArgs pEventArgs)
        {
            this.mMouseLocation = pEventArgs.Location;
        }

        private void cDoubleClicked(object pSender, EventArgs pEventArgs)
        {
            if(!this.mReadonly && sLabelRectangle.Contains(this.mMouseLocation))
            {
                AttachTextbox(this);
            }
        }

        private void cClicked(object pSender, EventArgs pEventArgs)
        {
            if (!sLabelRectangle.Contains(this.mMouseLocation))
            {
                DetachTextbox(false);
            }
        }

        private void cMouseEntered(object pSender, EventArgs pEventArgs)
        {
            // set our member flag for this to true
            this.mMouseOvered = true;
            
            // make sure this screen thumbnail is not selected
            if (this.BackgroundImage == null && this.mHighlightOnMouseOver)
            {
                // its not selected so apply the mouse over image
                this.BackgroundImage = sMouseOveredImage;
            }
        }

        private void cMouseLeave(object pSender, EventArgs pEventArgs)
        {
            // set our member flag for this to false
            this.mMouseOvered = false;

            // make sure this screen thumbnail currently has the mouse over image
            if (!this.mHighlighted && (this.BackgroundImage == sMouseOveredImage))
            {
                // it does have the mouse over image so replace it with nothing
                this.BackgroundImage = null;
            }
        }

        protected override void OnPaint(PaintEventArgs pEventArgs)
        {
            pEventArgs.Graphics.DrawImage(this.mScreen.ThumbnailImage, sImageRectangle.Location);
            pEventArgs.Graphics.DrawRectangle(Pens.Gray, sImageRectangle);
            pEventArgs.Graphics.DrawString(this.mScreen.Name, 
                sRenamingTextbox.Font, 
                Brushes.Black, 
                sLabelRectangle, 
                sStringFormat);
        }

        /// <summary>
        /// the screen associated with this screen thumbnail
        /// </summary>
        public Woz.Screen Screen
        {
            get
            {
                return this.mScreen;
            }
        }

        /// <summary>
        /// gets or sets whether the screen thumbnail can be renamed
        /// </summary>
        public bool Readonly
        {
            get
            {
                return this.mReadonly;
            }
            set
            {
                this.mReadonly = value;
            }
        }

        /// <summary>
        /// gets or sets whether to highlight on mouse over or not
        /// </summary>
        public bool HighlightOnMouseOver
        {
            get
            {
                return this.mHighlightOnMouseOver;
            }
            set
            {
                this.mHighlightOnMouseOver = value;
            }
        }

        /// <summary>
        /// gets the size of the entire screen thumbnail control
        /// </summary>
        public static Size ScreenThumbnailSize
        {
            get
            {
                return sSize;
            }
        }

        /// <summary>
        /// refresh the thumbnail image
        /// </summary>
        public void RefreshThumbnail()
        {
            using (Graphics graphics = this.CreateGraphics())
            {
                graphics.DrawImage(this.mScreen.ThumbnailImage, sImageRectangle.Location);
                graphics.DrawRectangle(Pens.Gray, sImageRectangle);
            }
        }

        /// <summary>
        /// attach a textbox to the screen thumbnail control
        /// </summary>
        /// <param name="pThumbnail">the screen thumbnail control to attach a textbox to</param>
        static private void AttachTextbox(Woz.ScreenThumbnail pThumbnail)
        {
            // first if its attached detach it
            if (sRenamingTextbox.Tag != null)
            {
                Woz.ScreenThumbnail.DetachTextbox(true);
            }

            // make sure its not readonly
            if (pThumbnail.Readonly)
            {
                return;
            }

            // attach it
            sRenamingTextbox.Tag = (object)pThumbnail;

            // move it to place
            sRenamingTextbox.Location = new Point(sLabelRectangle.Left, sLabelRectangle.Top - 3);

            // set the text
            sRenamingTextbox.Text = pThumbnail.mScreen.Name;

            // put the textbox where the label used to be
            pThumbnail.Controls.Add(sRenamingTextbox);

            // select the text so the user can edit
            sRenamingTextbox.Select();
        }

        /// <summary>
        /// detach a textbox from the screen thumbnail
        /// </summary>
        /// <param name="pIgnoreChange">specified whether to ignore the change</param>
        private static void DetachTextbox(bool pIgnoreChange)
        {
            // make sure its not detached already
            if (sRenamingTextbox.Tag == null)
            {
                return;
            }

            // save the screen thumbnail
            Woz.ScreenThumbnail screenThumbnail = sRenamingTextbox.Tag as Woz.ScreenThumbnail;

            // get the list of screen names
            List<string> screenNames = new List<string>();
            screenNames.Add("");
            foreach (Woz.Screen screen in Woz.ScreenManager.Instance.Screens)
            {
                // get all the screens names
                screenNames.Add(screen.Name.ToLower());
            }

            // detach it
            sRenamingTextbox.Tag = null;

            // check to see the new name is not already being used
            if (!pIgnoreChange && !screenNames.Contains(sRenamingTextbox.Text.ToLower()))
            {
                // fire a renamed event
                if (screenThumbnail.Renamed != null)
                {
                    screenThumbnail.Renamed.Invoke(screenThumbnail, sRenamingTextbox.Text);
                }
            }

            // remove the textbox from the thumbnail
            screenThumbnail.Controls.Remove(sRenamingTextbox);
        }

        /// <summary>
        /// represents the method that will handle when the screen thumbnail is renamed
        /// </summary>
        /// <param name="pSender">the object that sent this event</param>
        /// <param name="pNewName">the new name of the screen thumbnail</param>
        public delegate void RenameHandler(object pSender, string pNewName);

        /// <summary>
        /// fires when the screen thumbnail gets renamed
        /// </summary>
        public event RenameHandler Renamed;

        /// <summary>
        /// the screen associated with this screen thumbnail
        /// </summary>
        private Woz.Screen mScreen;
 
        /// <summary>
        /// whether or not this thumbnail slider can be renamed
        /// </summary>
        private bool mReadonly;

        /// <summary>
        /// background image that gets applied to selected thumbnails
        /// </summary>
        private static Image sSelectedImage;

        /// <summary>
        /// background image that gets applied to mouse overed thumbnails
        /// </summary>
        private static Image sMouseOveredImage;

        /// <summary>
        /// a textbox to perform renaming on
        /// </summary>
        private static TextBox sRenamingTextbox;

        private static Rectangle sImageRectangle;
        private static Rectangle sLabelRectangle;
        private static StringFormat sStringFormat;
        private static Padding sPadding;
        private static Size sSize;

        private Point mMouseLocation;
        private bool mSelected;
        private bool mMouseOvered;

        /// <summary>
        /// flag whether or not we are highlighted
        /// </summary>
        private bool mHighlighted;

        /// <summary>
        /// whether to automatically highlight on mouse over
        /// </summary>
        private bool mHighlightOnMouseOver;
    }
}
